
`include "common_header.verilog"

//  *************************************************************************
//  File : tx_ff_wctl
//  *************************************************************************
//  This program is controlled by a written license agreement.
//  Unauthorized Reproduction or Use is Expressly Prohibited. 
//  Copyright (c) 2009 MoreThanIP.com, Germany
//  Designed by : Daniel Koehler
//  info@morethanip.com
//  *************************************************************************
//  Description: Transmit FIFO Write Control with FIFO Protection
//  Version    : $Id: tx_ff_wctl.v,v 1.7 2013/09/19 13:01:58 dk Exp $
//  *************************************************************************

module tx_ff_wctl (

   reset_ff_tx_clk,
   ff_tx_clk,
   reset_tx_clk,
   tx_clk,
   sw_reset_wclk,
   tx_sop,
   tx_eop,
   tx_err,
   tx_wren,
   tx_rdy,
   tx_ovr_err,
   dff_afull,
   dff_sav,
   stat_empty,
   stat_wren,
   stat_err,
   stat_wcnt,
   dff_wren);

input   reset_ff_tx_clk;        //  Asynchronous Reset
input   ff_tx_clk;              //  Local Receive Clock
input   reset_tx_clk;           //  async reset fifo read side
input   tx_clk;                 //  read clock
input   sw_reset_wclk;          //  sync reset on write clock domain
input   tx_sop;                 //  Start of Packet
input   tx_eop;                 //  End of Packet
input   tx_err;                 //  Packet Error
input   tx_wren;                //  Link Layer FIFO Almost Full
output   tx_rdy;                //  FIFO Ready to Accept Data
output   tx_ovr_err;            //  FIFO overflow protection/truncation occured
input   dff_afull;              //  Data FIFO almost full (write clock domain)
input   dff_sav;                //  Data section available (MAC can start reading) (read clock)
input   stat_empty;             //  Status FIFO is empty (read clock)
output   stat_wren;             //  Status FIFO write Enable
output   stat_err;              //  error bit to write into status FIFO
output   [12:0] stat_wcnt;      //  number of words written for status fifo
output   dff_wren;              //  Data FIFO write Enable

wire    tx_rdy; 
wire    tx_ovr_err; 
wire    stat_wren; 
wire    stat_err; 
wire    [12:0] stat_wcnt; 
wire    dff_wren; 

reg     [12:0] ff_len_cnt; 

// TYPE STM_TYP:
parameter STM_TYP_IDLE          = 2'd 0;
parameter STM_TYP_IN_FRAME      = 2'd 1;
parameter STM_TYP_TRUNC_FRAME   = 2'd 2;

reg     [1:0] state; 
reg     [1:0] nextstate; 
reg     rd_avail;               //  read side fifo indicates data available to MAC (for lockup testing)
wire    rd_avail_w;             //  SYNC'ed to write clock domain
reg     [1:0] afull_cnt; 
reg     ovr_protect;            //  protect for overflow

//  -----------------------
//  get us the read side data available indication into write clock domain
//  -----------------------
always @(posedge reset_tx_clk or posedge tx_clk)
   begin : ravp
   if (reset_tx_clk == 1'b 1)
      begin
      rd_avail <= 1'b 0;	
      end
   else
      begin
        //  indicate that there is a read data available indication going to the MAC, hence
        //  no deadlock can occur.
      rd_avail <= dff_sav | ~stat_empty;	
      end
   end

mtip_xsync #(1) U_RAVS (
          .data_in(rd_avail),
          .reset(reset_ff_tx_clk),
          .clk(ff_tx_clk),
          .data_s(rd_avail_w));

always @(posedge reset_ff_tx_clk or posedge ff_tx_clk)
   begin : ovrp
   if (reset_ff_tx_clk == 1'b 1)
      begin
      ovr_protect <= 1'b 0;	//  get read side status into write side clock domain
      afull_cnt <= 2'b 00;	
      end
   else
      begin
      if (dff_afull == 1'b 0)
         begin
         afull_cnt <= 2'b 00;	
         end
      else if (dff_afull == 1'b 1 & tx_wren == 1'b 1 & afull_cnt != 2'b 11)
         begin
                //  give some latency to the application before we indicate overflow
         afull_cnt <= afull_cnt + 2'b 01;	
         end

        //  indicate overflow protection to happen. Frame will be truncated                        

      if (state == STM_TYP_IDLE & dff_afull==1'b 0)
         begin
         ovr_protect <= 1'b 0;	
         end
      else if (dff_afull == 1'b 1 & tx_wren == 1'b 1 & afull_cnt == 2'b 11 & (state != STM_TYP_IDLE) |
	       dff_afull == 1'b 1 & rd_avail_w == 1'b 0 |
	       ff_len_cnt > 13'b 1111111111100)
         begin
         ovr_protect <= 1'b 1;	
         end
      end
   end

//  Control State Machine
//  ---------------------
//  indicate overflow protection
//  This is indicated to allow stopping the write before a real overflow happens.
//  There are 2 conditions to protect for:
//  1) rdy deassertion was not respected by application
//  2) FIFO operates in Store&forward and frame is larger than the FIFO.
//  -----------------------------------------------------------------------------
always @(posedge reset_ff_tx_clk or posedge ff_tx_clk)
   begin : stmr
   if (reset_ff_tx_clk == 1'b 1)
      begin
      state <= STM_TYP_IDLE;	
      end
   else
      begin
      if (sw_reset_wclk == 1'b 1)
         begin
         state <= STM_TYP_IDLE;	
         end
      else
         begin
         state <= nextstate;	
         end
      end
   end

always @(state or tx_sop or tx_eop or tx_wren or ovr_protect or sw_reset_wclk)
   begin : stmc
   case (state)
   STM_TYP_IDLE:
      begin
      if (ovr_protect==1'b 0 & tx_wren == 1'b 1 & tx_sop == 1'b 1 & tx_eop == 1'b 0 & sw_reset_wclk == 1'b 0)
         begin
         nextstate = STM_TYP_IN_FRAME;	
         end
      else
         begin
         nextstate = STM_TYP_IDLE;	
         end
      end
   STM_TYP_IN_FRAME:
      begin
      if (tx_wren == 1'b 1 & tx_eop == 1'b 1)
         begin
        //  immediate return to allow for back-to-back writes
         nextstate = STM_TYP_IDLE;	
         end
      else if (ovr_protect == 1'b 1 )
         begin
         nextstate = STM_TYP_TRUNC_FRAME;	
         end
      else
         begin
         nextstate = STM_TYP_IN_FRAME;	
         end
      end
   STM_TYP_TRUNC_FRAME:
      begin
      if (tx_wren == 1'b 1 & tx_eop == 1'b 1)
         begin
         nextstate = STM_TYP_IDLE;	
         end
      else
         begin
         nextstate = STM_TYP_TRUNC_FRAME;	
         end
      end

   default:
      begin
      nextstate = STM_TYP_IDLE;	//  never reached
      end

   endcase
   end

//  word counter
//  ------------        
always @(posedge reset_ff_tx_clk or posedge ff_tx_clk)
   begin : lenp
   if (reset_ff_tx_clk == 1'b 1)
      begin
      ff_len_cnt <= {13{1'b 0}};	
      end
   else
      begin
      if (state == STM_TYP_IDLE)
         begin
         ff_len_cnt <= 13'b 0000000000001;	//  +1 as we suppress the first nextstate=in_frame
         end
      else if (nextstate == STM_TYP_IN_FRAME & tx_wren == 1'b 1 )
         begin
        //  note, when changing into TRUNC the last write is already counted, however not actually written to the FIFO.
        //  So the future write of eop is not needed to be added hence no need to consider any TRUNC states here
         ff_len_cnt <= ff_len_cnt + 13'b 0000000000001;	
         end
      end
   end

assign stat_wcnt = ff_len_cnt; 
//  Data FIFO write control
//  -----------------------
assign dff_wren = ((nextstate == STM_TYP_IN_FRAME) | (state != STM_TYP_IDLE & tx_eop == 1'b 1)) ? tx_wren : 1'b 0; //  we ALWAYS have to write the eop word!

//  status fifo write control
//  -------------------------
assign stat_err  = tx_err == 1'b 1 | state == STM_TYP_TRUNC_FRAME ? 1'b 1 : 1'b 0; 
assign stat_wren = state != STM_TYP_IDLE & tx_wren == 1'b 1 & tx_eop == 1'b 1 ? 1'b 1 : 1'b 0; 

//  fifo status indications
//  -----------------------
assign tx_rdy     = dff_afull == 1'b 0 | state == STM_TYP_TRUNC_FRAME ? 1'b 1 : 1'b 0; //  absorb all application data when truncation is ongoing 
assign tx_ovr_err = state == STM_TYP_TRUNC_FRAME ? 1'b 1 : 1'b 0; 

// -----------------------
// ASSERTIONS
// -----------------------

// synthesis translate_off
// synopsys translate_off

always @(posedge reset_ff_tx_clk or posedge ff_tx_clk)
begin
        if (reset_ff_tx_clk == 1'b 1)
        begin
        end
        else
        begin
                // indicate if a frame is dropped as it is written while FIFO full persisted at start of frame.
                if( state==STM_TYP_IDLE & ovr_protect & tx_wren & tx_sop )
                begin
                        $display("%t: ASSERT [tx_ff_wctl]: Frame written while FIFO is full (ff_tx_rdy not respected). Frame is dropped", $time);  
                end
        end
end


// synopsys translate_on
// synthesis translate_on

endmodule // module tx_ff_wctl

